#include "mainwindow.h"
#include "taskcreator.h"
#include "database.h"
#include "View.h"

#include "DataHelper.h"

#include "QKeyEvent"
#include "QDateTime"
#include "QInputDialog"
#include "QFileDialog"
#include "QAbstractItemView"
#include "QVBoxLayout"
#include "QHBoxLayout"
#include "QPushButton"
#include "QApplication"
#include "QFile"
#include "QDir"
#include "QLabel"
#include "QCheckBox"
#include "QPair"

#include "QMessageBox"
#include "QDebug"

#include "QThread"

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
{
    mainWidget = new QWidget();

    mainWidget->setMinimumSize(580, 200);

    QLabel l("[ЗАГРУЗКА]");
    l.show();

    QVBoxLayout *mainGroup = new QVBoxLayout();

    editTasks  = new QRadioButton("Шаблоны");
    editGroups = new QRadioButton("Группы");
    editTasks->setChecked(true);

    connect(editTasks,  &QPushButton::clicked, this, &MainWindow::on_editTasks_clicked);
    connect(editGroups, &QPushButton::clicked, this, &MainWindow::on_editGroups_clicked);

    QHBoxLayout *rbGroup = new QHBoxLayout;
    rbGroup->addWidget(editTasks);
    rbGroup->addWidget(editGroups);

    mainGroup->addLayout(rbGroup);

    treeWidget = new View();
    treeWidget->setColumnCount(5);
    treeWidget->setHeaderLabels({ "id", "Имя", "Группа", "Таймер", "Статус"});
    treeWidget->setDragDropMode(QAbstractItemView::InternalMove);
    treeWidget->setAcceptDrops(true);

    mainGroup->addWidget(treeWidget);

    QPushButton *howToUse     = new QPushButton("Работа с программой");
    QPushButton *selectTaskDB = new QPushButton("Указать Task.db");
    QPushButton *saveChanges  = new QPushButton("Сохранить изменения");

    QHBoxLayout *hbox = new QHBoxLayout;
    hbox->addWidget(howToUse);
    hbox->addWidget(selectTaskDB);
    hbox->addWidget(saveChanges);

    connect(howToUse,     &QPushButton::clicked, this, &MainWindow::on_howToUse_clicked);
    connect(selectTaskDB, &QPushButton::clicked, this, &MainWindow::on_selectTaskDB_clicked);
    connect(saveChanges,  &QPushButton::clicked, this, &MainWindow::on_saveChanges_clicked);

    mainGroup->addLayout(hbox);

    mainWidget->setLayout(mainGroup);
    mainWidget->setWindowTitle("mpSorter v" + version);
    mainWidget->show();

    connect(&colorSelector, &QColorDialog::colorSelected, this, &MainWindow::on_userSelectColor);

    connect(treeWidget, &View::create,   this, &MainWindow::on_create);
    connect(treeWidget, &View::moveUp,   this, &MainWindow::on_moveUp);
    connect(treeWidget, &View::moveDown, this, &MainWindow::on_moveDown);
    connect(treeWidget, &View::duplicate,this, &MainWindow::on_duplicate);
    connect(treeWidget, &View::remove,   this, &MainWindow::on_remove);
    connect(treeWidget, &View::restore,  this, &MainWindow::on_restore);
    connect(treeWidget, &View::updateId, this, &MainWindow::updateId);

    connect(treeWidget, &QTreeWidget::itemDoubleClicked, this, &MainWindow::on_treeWidget_itemDoubleClicked);

    connect(&taskCreator, &TaskCreator::sendTaskToParent, this, &MainWindow::getTaskFromChild);
    connect(&taskCreator, &TaskCreator::sendGroupToParent, this, &MainWindow::getGroupFromChild);

    tasks  = new QVector<Task*>;
    groups = new QVector<Group*>;

    l.close();
}

MainWindow::~MainWindow()
{ mainWidget->deleteLater(); }

void MainWindow::on_selectTaskDB_clicked(){    
    QString sFile = dlg.getOpenFileName();
    if(sFile.isNull()) return;

    isTaskDbFileSelected = true;

    QFileInfo f(sFile);
    saveDir = QDir(f.path());

    DataBase::init(&sFile);

    DataBase::selectGroups(groups);
    DataBase::selectTasks(tasks);

    if(tasks->count() == 0 || groups->count() == 0)
        return;

    for(auto &task : *tasks)
        task->group->usingCount++;

    if(editTasks->isChecked())
        showTasks();
    else
        showGroups();

    //--- проверка создан ли соответствующий файл для каждого задания
    bool isRemember         = false;
    bool isRememberNOButton = false;
    if(msg == nullptr){
        msg = new QMessageBox();
        QCheckBox *cb = new QCheckBox("Запомнить выбор");
        msg->setText("Проверка наличия файла задания");
        msg->setCheckBox(cb);

        connect(cb, &QCheckBox::stateChanged, this, [&](int state){
            isRemember = state;
        });
        msg->addButton("Да",  QMessageBox::AcceptRole);
        no_b = msg->addButton("Нет", QMessageBox::RejectRole);
    }
    for(auto &task : *tasks){
        QFile qf(saveDir.path() + QDir::separator() + "task-model" + QDir::separator() + *task->name + ".txt");
        qf.open(QIODevice::ReadOnly);
        if(!qf.exists()){
            if(isRemember && isRememberNOButton)//Запомнили 'Нет'
            { qf.close(); continue; }

            if(!isRemember){//Не запомнили 'Да'
                msg->setDetailedText("Задание [" + *task->name + "] не имеет соответствующего файла в save/task-model/, создать?");
                msg->exec();
                if (msg->clickedButton() == no_b) {
                    if(isRemember)
                        isRememberNOButton = true;
                    qf.close(); continue;
                }
            }
            qf.close(); qf.open(QIODevice::WriteOnly);
            qf.write("1"); qf.close();
        }
    }
}

void MainWindow::showTasks(){
    treeWidget->clear();

    for(auto &task : *tasks)
        createTreeTaskItem(task);
}
void MainWindow::showGroups(){
    treeWidget->clear();

    for(auto &group : *groups)
        createTreeGroupItem(group);
}

void MainWindow::on_saveChanges_clicked(){
    if(!isTaskDbFileSelected || tasks->count() == 0 || groups->count() == 0)
        return;

    treeWidget->setEnabled(false);

    QString dbFile = saveDir.path() + QDir::separator() + "Task.db";
    QFile::copy(dbFile, dbFile + "_old_" + currDateTime());

    DataBase::saveTasks(tasks);
    DataBase::saveGroups(groups);

    if(needToBackUp.count() > 0){//Делаем резервную копию удалённых заданий
        QDir bakDir(saveDir.path() + QDir::separator() + "task-model.bak");
        if(!bakDir.exists()) bakDir.mkpath(bakDir.path());

        for(auto &name : needToBackUp){
            QFile qf(saveDir.path()  + QDir::separator() + "task-model" + QDir::separator() + name + ".txt");
            qf.open(QIODevice::ReadWrite);
            qf.rename(saveDir.path()  + QDir::separator() + "task-model.bak" + QDir::separator() + name + "-" + currDateTime() + ".txt");
            qf.close();
        }
        needToBackUp.clear();
    }

    for(auto &task : *tasks){
        if(task->isRenamed){//У задания сменилось имя
            QFile qf(saveDir.path()  + QDir::separator() + "task-model" + QDir::separator() + task->firstName + ".txt");
            qf.open(QIODevice::ReadWrite);
            qf.rename(saveDir.path() + QDir::separator() + "task-model" + QDir::separator() + *task->name      + ".txt");
            qf.close();
            task->isRenamed = false;
            task->firstName = task->name;
        }
        else if(task->isNeedToCreateFile){//Добавлено новое задание
            QFile qf(saveDir.path()  + QDir::separator() + "task-model" + QDir::separator() + *task->name + ".txt");
            qf.open(QIODevice::WriteOnly);
            qf.write("1");
            qf.close();

            task->isNeedToCreateFile = false;
        }
    }

    treeWidget->setEnabled(true);
}

void MainWindow::on_howToUse_clicked(){
    if(!howToUse.isVisible())
        howToUse.show();

    QApplication::setActiveWindow(&howToUse);
}

void MainWindow::on_create(){
    if(!isTaskDbFileSelected){
        on_selectTaskDB_clicked();
        return;
    }

    if(!taskCreator.isVisible())
        taskCreator.show();

    taskCreator.setTaskCreation(editTasks->isChecked(), groups);

    QApplication::setActiveWindow(&taskCreator);
}
void MainWindow::on_moveUp(){
    QList<QTreeWidgetItem *> items = treeWidget->selectedItems();
    if(items.count() == 0) return;

    int index = treeWidget->indexOfTopLevelItem(items.first());

    if(index == 0)
        return;

    if(editGroups->isChecked()){
        Group *currGroup  = groups->at(index);
        Group *groupAbove = groups->at(index - 1);

        int tmpId      = currGroup->id;
        currGroup->id  = groupAbove->id;
        groupAbove->id = tmpId;

        groups->swapItemsAt(index, index - 1);

        showGroups();
        return;
    }

    tasks->at(index)->id     -= 1;
    tasks->at(index - 1)->id += 1;
    tasks->swapItemsAt(index, index - 1);

    QTreeWidgetItem *item = treeWidget->takeTopLevelItem(index - 1);
    items.first()->setData(0, 0, tasks->at(index - 1)->id);
    item->         setData(0, 0, tasks->at(index)->id);

    treeWidget->insertTopLevelItem(index, item);
}
void MainWindow::on_moveDown(){
    QList<QTreeWidgetItem *> items = treeWidget->selectedItems();
    if(items.count() == 0) return;

    int index    = treeWidget->indexOfTopLevelItem(items.first());
    int rowCount = treeWidget->topLevelItemCount();

    if(index == rowCount - 1)
        return;

    if(editGroups->isChecked()){
        Group *currGroup  = groups->at(index);
        Group *groupBelow = groups->at(index + 1);

        int tmpId      = currGroup->id;
        currGroup->id  = groupBelow->id;
        groupBelow->id = tmpId;

        groups->swapItemsAt(index, index + 1);

        showGroups();
        return;
    }

    tasks->at(index)->id     += 1;
    tasks->at(index + 1)->id -= 1;
    tasks->swapItemsAt(index, index + 1);

    QTreeWidgetItem *item = treeWidget->takeTopLevelItem(index + 1);
    items.first()->setData(0, 0, tasks->at(index + 1)->id);
    item->         setData(0, 0, tasks->at(index)->id);

    treeWidget->insertTopLevelItem(index, item);
}
void MainWindow::on_duplicate(){
    QTreeWidgetItem *item = treeWidget->currentItem();

    Task *task = taskById(item->data(0, 0).toInt());

    int times = QInputDialog::getInt(this, "Дублирование [" + *task->name + "]", "Укажите число дублей", 1, 1);

    tasks->reserve(tasks->count() + times);

    if(!saveDir.exists()) saveDir.mkpath(saveDir.path());
    QDir taskModel(saveDir.path() + QDir::separator() + "task-model" + QDir::separator());
    if(!taskModel.exists()) taskModel.mkpath(taskModel.path());

    //    bool isRemember         = false;
    //    bool isRememberNOButton = false;
    //    if(msg1 == nullptr){
    //        QCheckBox   *cb  = new QCheckBox("Запомнить выбор");
    //        msg1 = new QMessageBox();
    //        msg1->setText("Дублирование [" + *task->name + "]");
    //        msg1->setCheckBox(cb);

    //        connect(cb, &QCheckBox::stateChanged, this, [&](int state){
    //            isRemember = state;
    //        });
    //        msg1->addButton("Да", QMessageBox::AcceptRole);
    //        no_b1 = msg1->addButton("Нет", QMessageBox::RejectRole);
    //    }
    int index = treeWidget->indexOfTopLevelItem(item);
    int tasksExist = 0;
    for(int i = 1; i <= times; ++i){
        QString newName = *task->name + "_" + QString::number(i);
        if(isTaskNameExists(&newName)){
            //            if(isRemember && isRememberNOButton)//Запомнили 'Нет'
            //            { ++tasksExist; continue; }

            //            if(!isRemember){//Не запомнили 'Да'
            //                msg1->setDetailedText("Задание [" + *task->name + "] уже существует, желаете перезаписать?");
            //                msg1->exec();
            //                if (msg1->clickedButton() == no_b1) {
            //                    if(isRemember)
            //                        isRememberNOButton = true;
            ++tasksExist;
            //                    continue;
            //                }
            //                else {}//task->isNeedToCreateFile = true;
            //            }
        }
        else{
            Task *t = createTask(task->id + i - tasksExist, task->timer, &newName, task->group, task->status);
            tasks->insert(index + i - tasksExist, t);
        }
    }

    task->group->usingCount += times - tasksExist;

    for(int i = task->id + times - tasksExist; i < tasks->count(); ++i)
        tasks->at(i)->id = i + 1;

    if(tasksExist != 0)
        QMessageBox::information(this, "Дублирование [" + *task->name + "]", "[" + QString::number(tasksExist) + "]" + " заданий не было создано, так как они уже существуют");

    showTasks();
}
void MainWindow::on_remove(){
    QTreeWidgetItem *item = treeWidget->currentItem();
    if(item == nullptr)
        return;

    int index = treeWidget->indexOfTopLevelItem(item);
    if(editTasks->isChecked()){
        Task *task = tasks->at(index);
        if(task->isRemoveCandidate){
            needToBackUp.append(task->name);

            task->group->usingCount -= 1;

            delete tasks->takeAt(index);
            delete treeWidget->takeTopLevelItem(index);

            for(int i = index; i < tasks->count(); ++i){
                tasks->at(i)->id -= 1;
                treeWidget->topLevelItem(i)->setData(0, 0, tasks->at(i)->id);
            }
        }
        else{
            task->isRemoveCandidate = true;
            item->setBackground(0, Qt::red);
        }
    }
    else if(editGroups->isChecked()){
        Group *group = groups->at(index);
        if(group->usingCount > 0){
            QMessageBox::information(this, "Удаление группы", "Группа используется [" + QString::number(group->usingCount) + "] раз");
            return;
        }
        if(group->isRemoveCandidate){
            delete groups->takeAt(index);
            delete treeWidget->takeTopLevelItem(index);
        }
        else{
            group->isRemoveCandidate = true;
            item->setBackground(0, Qt::red);
        }
    }
}
void MainWindow::on_restore(){
    QTreeWidgetItem *item = treeWidget->currentItem();
    if(item == nullptr)
        return;

    int index = treeWidget->indexOfTopLevelItem(item);
    if(editTasks->isChecked()){
        tasks->at(index)->isRemoveCandidate = false;
    }
    else if(editGroups->isChecked()){
        groups->at(index)->isRemoveCandidate = false;
    }
    item->setBackground(0, QBrush());
}

void MainWindow::createTreeTaskItem(Task *task){
    QTreeWidgetItem *item = new QTreeWidgetItem(treeWidget);
    item->setData(0, 0,    task->id);//id
    item->setData(1, 0,    *task->name);//Имя
    item->setData(2,    0, *task->group->name);
    item->setBackground(2, task->group->color);
    item->setData(3, 0,    task->timer);//Таймер
    item->setData(4, 0,    task->status ? "ON" : "OFF");//Статус

    if(task->isRemoveCandidate)
        item->setBackground(0, Qt::red);

    item->setFlags(Qt::ItemIsSelectable |
                   Qt::ItemIsUserCheckable |
                   Qt::ItemIsEnabled |
                   Qt::ItemIsDragEnabled );

    int rowsCount = treeWidget->topLevelItemCount();
    treeWidget->insertTopLevelItem(rowsCount, item);
}
void MainWindow::createTreeGroupItem(Group *group){
    QTreeWidgetItem *item = new QTreeWidgetItem(treeWidget);

    item->setData(0, 0,    group->id);//id
    item->setData(1, 0,    *group->name);//Имя
    item->setBackground(2, group->color);
    item->setData(3, 0,    group->usingCount);

    item->setFlags(Qt::ItemIsSelectable |
                   Qt::ItemIsUserCheckable |
                   Qt::ItemIsEnabled);

    int rowsCount = treeWidget->topLevelItemCount();
    treeWidget->insertTopLevelItem(rowsCount, item);
}

Task* MainWindow::taskById(int id){
    for(auto &task : *tasks){
        if(task->id == id)
            return task;
    }
    return nullptr;
}

Task* MainWindow::taskByName(QString *name){
    for(auto &task : *tasks){
        if(*task->name == name)
            return task;
    }
    return nullptr;
}

Group* MainWindow::groupById(int id){
    for(auto &group : *groups){
        if(group->id == id)
            return group;
    }
    return nullptr;
}

Group* MainWindow::groupByName(QString *name){
    for(auto &group : *groups){
        if(*group->name == name)
            return group;
    }
    return nullptr;
}

bool MainWindow::isTaskNameExists(QString *name){
    for(auto &task : *tasks){
        if(*task->name == name)
            return true;
    }
    return false;
}
Task* MainWindow::createTask(int id, int timer, QString *name, Group *group, bool status = true){
    Task* task = new Task;

    task->id     = id;
    task->timer  = timer;
    task->name   = new QString(*name);
    task->firstName = task->name;
    task->group  = group;
    task->status = status;
    task->isNeedToCreateFile = true;

    return task;
}
Group* MainWindow::createGroup(int id, QString *name, QColor *color){
    Group *group = new Group;

    group->id    = id;
    group->name  = new QString(*name);
    group->color = *color;

    return group;
}

QString MainWindow::currDateTime(){
    return (QDate::currentDate().toString("dd.MM.yyyy") + "." + QTime::currentTime().toString("hh.mm.s"));
}

void MainWindow::on_editTasks_clicked(){
    treeWidget->setColumnCount(5);
    treeWidget->setHeaderLabels({ "id", "Имя", "Группа", "Таймер", "Статус"});

    if(tasks->count() == 0)
        return;

    showTasks();
}
void MainWindow::on_editGroups_clicked(){
    treeWidget->setColumnCount(4);
    treeWidget->setHeaderLabels({ "id", "Имя", "Цвет", "Число использований"});

    if(groups->count() == 0)
        return;

    showGroups();
}

void MainWindow::on_treeWidget_itemDoubleClicked(QTreeWidgetItem *item, int column){
    if(column == 1){//Два раза кликнули на Имя
        QString currentName = item->data(1, 0).toString();
        QString *newName    = new QString(QInputDialog::getText(this, tr("Имя"), tr("Изменение имени"), QLineEdit::Normal, currentName));
        if(newName->length() > 0 && newName != currentName){
            if(isTaskNameExists(newName)){
                QMessageBox::information(this, "Изменение имени", "Имя уже используется");
                delete newName; return;
            }

            if(editTasks->isChecked()){
                if(needToBackUp.contains(newName)){
                    auto answer = QMessageBox::information(this, "Изменение имени", "Обнаружено совпадение с заданием требующим сохранение. Выполнить сохранение?", QMessageBox::Yes|QMessageBox::No);

                    if(answer == QMessageBox::Yes)
                        on_saveChanges_clicked();
                    else { delete newName; return; }
                }

                int index = treeWidget->indexOfTopLevelItem(item);
                tasks->at(index)->rename(newName);
            }
            else{
                if(groupByName(newName) != nullptr){
                    QMessageBox::information(this, "Изменение имени", "Имя уже используется");
                    delete newName; return;
                }
                groupById(item->data(0, 0).toInt())->name = newName;
            }

            item->setData(column, 0, *newName);
        }
        else delete newName;
    }
    else if(column == 2){//Два раза кликнули на Group или Color
        if(editTasks->isChecked()){
            Task *task = taskById(item->data(0, 0).toInt());
            QVector<QString> names(groups->count());
            for(auto &group : *groups)
                names.append(*group->name);

            QString selItem = QInputDialog::getItem(this, "Группа", "Изменение группы задания", names.toList(), names.indexOf(*task->group->name), false);
            Group *selectedGroup = groupByName(&selItem);
            if(task->group != selectedGroup){
                task->group->usingCount--;
                selectedGroup->usingCount++;

                task->group = selectedGroup;

                item->setData(column, 0,    *selectedGroup->name);//Группа
                item->setBackground(column, selectedGroup->color);
            }
        }
        else{//выбранна группа и нажали на Color
            int id = item->data(0, 0).toInt();
            colorSelector.setCurrentColor(groupById(id)->color);
            colorSelector.show();
        }
    }
    else if(column == 3){//Два раза кликнули на Timer
        if(!editTasks->isChecked())
            return;

        int currentTimer = item->data(column, 0).toInt();
        int newTimer     = QInputDialog::getInt(this, "Таймер", "Изменение таймера задания", currentTimer, 1);
        if(newTimer != currentTimer){
            int index = treeWidget->indexOfTopLevelItem(item);

            tasks->at(index)->timer = newTimer;

            item->setData(column, 0, newTimer);
        }
    }
    else if(column == 4 && editTasks->isChecked()){//Два раза кликнули на Статус
        int index    = treeWidget->indexOfTopLevelItem(item);
        Task *task   = tasks->at(index);
        task->status = !task->status;

        item->setData(4, 0, tasks->at(index)->status ? "ON" : "OFF");
    }
}

void MainWindow::on_userSelectColor(const QColor &color){
    QTreeWidgetItem *item = treeWidget->currentItem();
    int id = item->data(0, 0).toInt();

    groupById(id)->color = color;
    item->setBackground(2, color);
}

void MainWindow::updateId(const QPoint *point) {
    QTreeWidgetItem *item = treeWidget->itemAt(*point);
    int movedTo;
    if(item == nullptr){
        item = treeWidget->currentItem();
        movedTo = treeWidget->topLevelItemCount() - 1;
    }
    else movedTo = treeWidget->indexOfTopLevelItem(item);

    Task *t = tasks->takeAt(item->data(0, 0).toInt() - 1);

    tasks->insert(movedTo, t);
    for(int i = item->data(0, 0).toInt() - 1; i < tasks->count(); ++i){
        tasks->at(i)->id = i + 1;
    }

    showTasks();
}

void MainWindow::getTaskFromChild(QString *name, int groupId, int timer, int times){
    if(needToBackUp.contains(name)){
        auto answer = QMessageBox::information(this, "Добавление задания", "Имя совпало с заданием требующим сохранения. Выполнить сохранение?", QMessageBox::Yes|QMessageBox::No);

        if(answer == QMessageBox::Yes)
            on_saveChanges_clicked();
        else return;
    }

    if(tasks->capacity() < tasks->count() + times){
        tasks->reserve(tasks->count() + times);
    }

    int lasTaskId = tasks->count() ? tasks->last()->id : 1;
    Group *group = groupById(groupId);
    if(group == nullptr) return;

    if(times == 1){
        if(isTaskNameExists(name)){
            QMessageBox::information(this, "Добавление задания", "Имя уже используется");
            return;
        }

        Task *task = createTask(++lasTaskId, timer, name, group);
        tasks->append(task);

        createTreeTaskItem(task);
    }
    else{
        QVector<QString*> validNames(times);
        for(int i = 1; i <= times; ++i)
            validNames[i - 1] = new QString(*name + "_" + QString::number(i));

        int badNames = 0;
        for(auto &task : *tasks){
            if(task->name->contains('_')){
                for(int i = 0; i < validNames.count(); ++i){
                    if(*task->name == validNames.at(i)){
                        delete validNames.takeAt(i);
                        ++badNames;
                        break;
                    }
                }
            }
            if(validNames.count() == 0) return;
        }
        for(auto &tname : validNames){
            Task *tnew = createTask(++lasTaskId, timer, tname, group);
            tasks->append(tnew);
            createTreeTaskItem(tnew);
        }
        group->usingCount += validNames.count();

        if(badNames != 0)
            QMessageBox::information(this, "Добавление задания", "[" + QString::number(badNames) + "] заданий не было создано, такак как имена совпали с существующими дублями");
    }
}
void MainWindow::getGroupFromChild(QString *name, QColor *color){
    if(groupByName(name) != nullptr){
        QMessageBox::information(this, "Добавление группы", "Имя уже используется");
        return;
    }

    int lastGroupId = groups->count() ? groups->last()->id : 1;

    Group *group = createGroup(lastGroupId + 1, name, color);

    groups->append(group);

    createTreeGroupItem(group);
}
